Wasserbau 4 – Übung 02#
Aufgabe 1 – Interaktiver Wehrbedienungsplan#
Dieses interaktive Notebook erlaubt es, Parameter für eine Wehranlage selbst zu variieren und die Auswirkungen auf hydraulische Kenngrößen zu untersuchen.
# Konstanten
b_klappe = 7.2 # Breite pro Klappe [m]
g = 9.81 # Erdbeschleunigung [m/s²]
import ipywidgets as widgets
from ipywidgets import interact
from IPython.display import display
# Interaktive Eingabe der Parameter
mu_widget = widgets.FloatSlider(value=0.65, min=0.4, max=1.0, step=0.01, description='μ:')
n_widget = widgets.IntSlider(value=4, min=1, max=5, step=1, description='Klappen n:')
Q_widget = widgets.FloatSlider(value=440, min=10, max=500, step=10, description='Q [m³/s]:')
h1_widget = widgets.FloatSlider(value=1.19, min=1.0, max=2.0, step=0.005, description='h1 [m]:')
#display(mu_widget, n_widget, Q_widget, h1_widget)
def berechne_interaktiv(mu, n, Q, h1):
A = b_klappe * n
v1 = Q / (A * h1)
Fr = v1 / (g * h1)**0.5
h2_erf = (h1 / 2) * ((8 * Fr**2 + 1)**0.5 - 1)
print(f"Froude-Zahl Fr = {Fr:.2f}")
print(f"Erforderliche Tiefe h2_erf = {h2_erf:.2f} m")
interact(berechne_interaktiv, mu=mu_widget, n=n_widget, Q=Q_widget, h1=h1_widget)
<function __main__.berechne_interaktiv(mu, n, Q, h1)>
import pandas as pd
import plotly.express as px
import ipywidgets as widgets
from IPython.display import display
# Gegebene Daten für h2_erf und Abfluss bei verschiedenen Klappenanzahlen
data = {
"h2_erf [m]": [1.49, 2.67, 4.09, 5.76],
"n=1": [6, 20, 50, 110],
"n=2": [12, 40, 100, 220],
"n=3": [18, 60, 150, 330],
"n=4": [24, 80, 200, 440],
"n=5": [30, 100, 250, 550],
}
# Erstellen eines Pandas DataFrames aus dem Dictionary
df = pd.DataFrame(data)
# Funktion zum Anzeigen des Diagramms
def zeige_diagramm():
# Erstellen einer Liste aller Spalten für die Klappenanzahlen
spalten = [f"n={n}" for n in range(1, 6)]
# Erstellen des Diagramms mit Plotly Express
fig = px.line(df, y="h2_erf [m]", x=spalten,
title="Wehrbedienungsplan (Alle Klappen)",
labels={"h2_erf [m]": "h2_erf [m]", "value": "Abfluss Q [m³/s]", "variable": "Klappenanzahl"},
markers=True,
range_x=[0, 550],
range_y=[0, 6])
# Anpassen des Layouts des Diagramms
fig.update_layout(
yaxis_title="h2_erf [m]",
xaxis_title="Abfluss Q [m³/s]",
yaxis_range=[0, 6],
xaxis_range=[0, 550],
yaxis_gridcolor='lightgrey',
xaxis_gridcolor='lightgrey',
plot_bgcolor='white'
)
# Anzeigen des Diagramms
fig.show()
# Anzeigen des Diagramms (ohne interaktives Widget)
zeige_diagramm()
Plotly-Diagramme sind ein wertvolles Werkzeug, um hydraulische Daten interaktiv zu erkunden. Anders als statische Grafiken ermöglichen sie es, durch einfaches Hovern mit der Maus präzise Werte an einzelnen Datenpunkten abzulesen. Für Detailanalysen könnt ihr in bestimmte Bereiche zoomen oder das Diagramm verschieben. Ein wesentliches Feature ist die interaktive Legende: Einzelne Datensätze lassen sich durch einen Klick gezielt ein- oder ausblenden. Der Doppelklick auf einen Eintrag in der Legende isoliert diesen Datensatz, was den Vergleich mit anderen Datensätzen vereinfacht.
✍ Aufgabe:#
Variieren Sie den Abfluss Q und beobachten Sie, wie sich die erforderliche Wassertiefe h2_erf verändert.
Was passiert bei hoher Froude-Zahl?
Wie verändert sich der Wehrbedienungsplan bei geringer Klappenzahl?
import pandas as pd
import plotly.express as px
import dash
from dash import dcc, html, Input, Output
# Daten definieren
abflusskurve_data = {
"Q": [0, 20, 24, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300, 320, 340, 360, 380, 400, 420, 440],
"hu": [0, 0.6, 0.66, 0.9, 1.15, 1.37, 1.59, 1.78, 1.97, 2.13, 2.3, 2.45, 2.6, 2.75, 2.89, 3.02, 3.15, 3.28, 3.4, 3.52, 3.64, 3.76, 3.82, 3.9]
}
df_abflusskurve = pd.DataFrame(abflusskurve_data)
rp = [(24, 0.66), (80, 1.37), (200, 2.45), (440, 3.9)]
toleranz_Q = 10
toleranz_hu = 0.1
# Dash-App erstellen
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Graph(id='abfluss-plot', config={'displayModeBar': False}),
html.Div(id='rueckmeldung', style={'font-weight': 'bold', 'font-size': '18px', 'margin-top': '10px'}),
html.Div(id='korrekte-punkte', style={'margin-top': '10px'}),
html.Div(id='erfolg-nachricht', style={'font-weight': 'bold', 'font-size': '20px', 'color': 'green', 'margin-top': '20px'})
])
korrekt_ausgewaehlt = []
@app.callback(
[Output('abfluss-plot', 'figure'),
Output('rueckmeldung', 'children'),
Output('korrekte-punkte', 'children'),
Output('erfolg-nachricht', 'children')],
Input('abfluss-plot', 'clickData')
)
def update_plot(clickData):
global korrekt_ausgewaehlt
fig = px.line(df_abflusskurve, x='Q', y='hu', title="Finde die richtigen Punkte!",
labels={'Q': 'Abfluss Q [m³/s]', 'hu': 'hu [m]'}, line_shape='linear')
fig.update_traces(line=dict(color='blue'))
rueckmeldung = "Klicke auf einen Punkt in der Grafik."
erfolg_nachricht = ""
if clickData:
clicked_x = clickData['points'][0]['x']
clicked_y = clickData['points'][0]['y']
richtig = any(abs(clicked_x - Q) <= toleranz_Q and abs(clicked_y - hu) <= toleranz_hu for Q, hu in rp)
if richtig:
naechster_punkt = next((p for p in rp if abs(clicked_x - p[0]) <= toleranz_Q and abs(clicked_y - p[1]) <= toleranz_hu), None)
if naechster_punkt and naechster_punkt not in korrekt_ausgewaehlt:
farbe = 'green'
rueckmeldung = "✅ Punkt richtig!"
korrekt_ausgewaehlt.append(naechster_punkt)
if len(korrekt_ausgewaehlt) == len(rp):
erfolg_nachricht = "Herzlichen Glückwunsch - alle Punkte gefunden!"
else:
farbe = 'orange'
rueckmeldung = "Dieser Punkt wurde bereits ausgewählt."
else:
farbe = 'red'
rueckmeldung = "❌ Punkt falsch!"
fig.add_scatter(x=[clicked_x], y=[clicked_y], mode='markers', marker=dict(size=12, color=farbe))
# Füge alle korrekt ausgewählten Punkte hinzu
for x, y in korrekt_ausgewaehlt:
fig.add_scatter(x=[x], y=[y], mode='markers', marker=dict(size=12, color='green'))
korrekte_punkte_text = [
html.Div([
html.Span("h_u,", style={'font-size': '18px'}),
html.Span(f"{int(x)}", style={'font-size': '14px', 'vertical-align': 'sub'}),
html.Span(f" = {y:.2f}", style={'font-size': '18px'})
]) for x, y in korrekt_ausgewaehlt
]
return fig, rueckmeldung, korrekte_punkte_text if korrekte_punkte_text else "", erfolg_nachricht
if __name__ == '__main__':
app.run(debug=False)
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
Cell In[4], line 85
82 return fig, rueckmeldung, korrekte_punkte_text if korrekte_punkte_text else "", erfolg_nachricht
84 if __name__ == '__main__':
---> 85 app.run(debug=False)
File ~\anaconda3\envs\hausarbeit_wb4\lib\site-packages\dash\dash.py:2257, in Dash.run(self, host, port, proxy, debug, jupyter_mode, jupyter_width, jupyter_height, jupyter_server_url, dev_tools_ui, dev_tools_props_check, dev_tools_serve_dev_bundles, dev_tools_hot_reload, dev_tools_hot_reload_interval, dev_tools_hot_reload_watch_interval, dev_tools_hot_reload_max_retry, dev_tools_silence_routes_logging, dev_tools_disable_version_check, dev_tools_prune_errors, **flask_run_options)
2254 extra_files.append(path)
2256 if jupyter_dash.active:
-> 2257 jupyter_dash.run_app(
2258 self,
2259 mode=jupyter_mode,
2260 width=jupyter_width,
2261 height=jupyter_height,
2262 host=host,
2263 port=port,
2264 server_url=jupyter_server_url,
2265 )
2266 else:
2267 self.server.run(host=host, port=port, debug=debug, **flask_run_options)
File ~\anaconda3\envs\hausarbeit_wb4\lib\site-packages\dash\_jupyter.py:405, in JupyterDash.run_app(self, app, mode, width, height, host, port, server_url)
403 display(HTML(msg))
404 else:
--> 405 raise final_error
File ~\anaconda3\envs\hausarbeit_wb4\lib\site-packages\dash\_jupyter.py:392, in JupyterDash.run_app(self, app, mode, width, height, host, port, server_url)
389 raise err
391 try:
--> 392 wait_for_app()
394 if self.in_colab:
395 JupyterDash._display_in_colab(dashboard_url, port, mode, width, height)
File ~\anaconda3\envs\hausarbeit_wb4\lib\site-packages\retrying.py:56, in retry.<locals>.wrap.<locals>.wrapped_f(*args, **kw)
54 @six.wraps(f)
55 def wrapped_f(*args, **kw):
---> 56 return Retrying(*dargs, **dkw).call(f, *args, **kw)
File ~\anaconda3\envs\hausarbeit_wb4\lib\site-packages\retrying.py:266, in Retrying.call(self, fn, *args, **kwargs)
263 if self.stop(attempt_number, delay_since_first_attempt_ms):
264 if not self._wrap_exception and attempt.has_exception:
265 # get() on an attempt with an exception should cause it to be raised, but raise just in case
--> 266 raise attempt.get()
267 else:
268 raise RetryError(attempt)
File ~\anaconda3\envs\hausarbeit_wb4\lib\site-packages\retrying.py:301, in Attempt.get(self, wrap_exception)
299 raise RetryError(self)
300 else:
--> 301 six.reraise(self.value[0], self.value[1], self.value[2])
302 else:
303 return self.value
File ~\anaconda3\envs\hausarbeit_wb4\lib\site-packages\six.py:724, in reraise(tp, value, tb)
722 if value.__traceback__ is not tb:
723 raise value.with_traceback(tb)
--> 724 raise value
725 finally:
726 value = None
File ~\anaconda3\envs\hausarbeit_wb4\lib\site-packages\retrying.py:251, in Retrying.call(self, fn, *args, **kwargs)
248 self._before_attempts(attempt_number)
250 try:
--> 251 attempt = Attempt(fn(*args, **kwargs), attempt_number, False)
252 except:
253 tb = sys.exc_info()
File ~\anaconda3\envs\hausarbeit_wb4\lib\site-packages\dash\_jupyter.py:383, in JupyterDash.run_app.<locals>.wait_for_app()
381 if res != "Alive":
382 url = f"http://{host}:{port}"
--> 383 raise OSError(
384 f"Address '{url}' already in use.\n"
385 " Try passing a different port to run."
386 )
387 except requests.ConnectionError as err:
388 _get_error()
OSError: Address 'http://127.0.0.1:8050' already in use.
Try passing a different port to run.